import { CloseCircleFilled } from '@ant-design/icons'
import { useRefValue } from '@gnlab/utils/env-react'
import { Button, Radio, Input, Checkbox } from 'antd'
import c from 'classnames'
import { OrderedMap } from 'immutable'
import debounce from 'lodash/debounce'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList as List } from 'react-window'
import { ImportantEditModal } from '@/desktop/components'
import  defaultAvatar  from '@/desktop/assets/avatar.jpg'
import { useStreams } from '@/lib/utils'
import services from '@/services'
import { ContactType, type Contact } from '@/services/contacts'
import { type QywxStatusInfo } from '@/services/qywxs'
import { defaultTypes, useContacts } from './hooks'
import './index.module.less'

type ListData = { contact: Contact; choosed: boolean; choosedNo: number; top: boolean }[]

export default function ChooseContacts(props: {
  visible: boolean
  title: string // 对话框标题
  ownerWxid?: string // 选中哪个企业微信的联系人，不指定则为所有企业微信的联系人
  types?: ContactType[] // 可选中的联系人类型列表（默认为 客户 和 员工）
  disabledWxids?: string[] // 禁止选中的联系人的 wxid
  limit?: number // 最大可选择几个人
  onFinish: (contacts: Contact[]) => Promise<void> | void // 选择完成调用此回调
  onCancel: () => void // 取消选择调用此回调
}) {
  const { visible, title, onCancel, ...rest } = props

  return <ImportantEditModal title={title} width="700px" onCancel={onCancel} open={visible}>
    <Content {...rest} onCancel={onCancel} />
  </ImportantEditModal>
}

function Content(props: {
  ownerWxid?: string
  types?: ContactType[]
  disabledWxids?: string[]
  limit?: number
  onFinish: (contacts: Contact[]) => Promise<void> | void
  onCancel: () => void
}) {
  const { ownerWxid, types = defaultTypes, disabledWxids, limit = null, onFinish, onCancel } = props
  const [qywxMap, tops] = useStreams(services.qywxs.alives, services.contacts.tops.items)

  const [type, setType] = useState(types[0]!)
  const [search, setSearch] = useState('')
  const contacts = useContacts(ownerWxid ?? null, type, search)

  const [choosed, setChoosed] = useState(OrderedMap<string, Contact>())
  const choosedRef = useRefValue(choosed)
  const switchChoose = useCallback(
    (contact: Contact) => {
      const current = choosedRef.current
      if (current.has(contact.id)) setChoosed(current.remove(contact.id))
      else setChoosed(current.set(contact.id, contact))
    },
    [choosedRef],
  )

  const [submitting, setSubmitting] = useState(false)
  const submit = useCallback(async () => {
    const res = onFinish([...choosed.values()])
    if (res) {
      setSubmitting(true)
      await res
      setSubmitting(false)
    }
  }, [choosed, onFinish])

  // ===================================================

  // external 情况下因为是调接口搜索，设置一个 throttle 来更新 search
  // 其他情况无需考虑性能，则实时搜索
  const debounceSetSearch = useMemo(() => debounce(setSearch, 300), [])
  const searchOnChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      if (type === ContactType.external) debounceSetSearch(e.target.value)
      else setSearch(e.target.value)
    },
    [type, debounceSetSearch],
  )

  const typeOptions = useMemo(
    () =>
      [
        types.includes(ContactType.external)
          ? { label: '客户', value: ContactType.external }
          : null,
        types.includes(ContactType.group) ? { label: '群', value: ContactType.group } : null,
        types.includes(ContactType.internal)
          ? { label: '员工', value: ContactType.internal }
          : null,
      ].filter((v): v is { label: string; value: ContactType } => v !== null),
    [types],
  )

  // 渲染函数要尽可能少变动，因为它每次重新生成，都会导致列表元素彻底重新渲染
  // 不常变动的数据 bind 到渲染函数上，经常变动的，写入到 data 里
  const listData = useMemo(() => {
    return [...contacts]
      .sort((a, b) => (tops.has(a.id) ? -1 : 1) - (tops.has(b.id) ? -1 : 1))
      .map(contact => ({
        contact,
        choosed: choosed.has(contact.id),
        choosedNo: choosed.size,
        top: tops.has(contact.id),
      }))
  }, [contacts, choosed, tops])
  const boundRenderContact = useMemo(
    () => RenderContact.bind(null, qywxMap, switchChoose, limit, disabledWxids ?? []),
    [switchChoose, limit, disabledWxids, qywxMap],
  )

  return <>
    <div styleName="content">
      <div styleName="choose-column">
        <div styleName="types">
          <Radio.Group
            optionType="button"
            options={typeOptions}
            onChange={e => setType(e.target.value as ContactType)}
            value={type}
          />
        </div>
        <div styleName="search">
          <Input onChange={searchOnChange} placeholder="输入搜索关键词" />
        </div>
        <div styleName="contacts">
          <AutoSizer>
            {({ height, width }) => <List
              height={height}
              width={width}
              itemCount={listData.length}
              itemData={listData}
              itemKey={getItemKey}
              itemSize={55}
            >
              {boundRenderContact}
            </List>}
          </AutoSizer>
        </div>
      </div>

      <div styleName="choosed-column">
        <header>
          {choosed.size ? `已选中了 ${choosed.size} 个联系人` : <div>尚未选中联系人</div>}
        </header>
        <div styleName="choosed">
          {[...choosed.values()].map(contact => <ChoosedContact
            key={contact.id}
            contact={contact}
            switchChoose={switchChoose}
            qywxMap={qywxMap}
          />)}
        </div>
      </div>
    </div>

    <footer>
      <Button onClick={onCancel}>取消</Button>
      <Button type="primary" disabled={!choosed.size} onClick={submit} loading={submitting}>
        确定
      </Button>
    </footer>
  </>
}

function getItemKey(index: number, data: ListData) {
  return data[index]?.contact.id ?? ''
}

function RenderContact(
  qywxMap: OrderedMap<string, QywxStatusInfo>,
  switchChoose: (contact: Contact) => void,
  limit: number | null,
  disabledWxids: string[],
  props: {
    index: number
    data: ListData
    style: React.CSSProperties
  },
) {
  const { index, data, style } = props
  const { contact, choosed, choosedNo, top } = data[index]!

  const disabled = disabledWxids.includes(contact.userId)
  const limited = limit !== null && choosedNo >= limit && !choosed

  const contactSwitchChoose = useCallback(() => {
    if (disabled || limited) return
    switchChoose(contact)
  }, [disabled, limited, contact, switchChoose])

  return <div
    styleName={c('contact', disabled && 'disabled', top && 'top')}
    style={style}
    onClick={contactSwitchChoose}
  >
    <Checkbox checked={choosed} disabled={disabled || limited} />
    <img src={contact.avatar || defaultAvatar} />
    <div styleName="info">
      <div styleName="name-row">
        {contact.remark ? <span styleName="remark">{contact.remark}</span> : null}
        <span styleName="name">{contact.name}</span>
      </div>
      <div styleName="qywx">{qywxMap.get(contact.ownerWxid)?.friendlyFullName}</div>
    </div>
  </div>
}

function ChoosedContact(props: {
  contact: Contact
  switchChoose: (contact: Contact) => void
  qywxMap: OrderedMap<string, QywxStatusInfo>
}) {
  const { contact, switchChoose, qywxMap } = props

  return <div styleName="contact">
    <img src={contact.avatar || defaultAvatar} />
    <div styleName="info">
      <div styleName="name-row">
        {contact.remark ? <span styleName="remark">{contact.remark}</span> : null}
        <span styleName="name">{contact.name}</span>
      </div>
      <div styleName="qywx">{qywxMap.get(contact.ownerWxid)?.friendlyFullName}</div>
    </div>
    <CloseCircleFilled styleName="remove" onClick={() => switchChoose(contact)} />
  </div>
}
